﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.ComponentModel;
using System.Threading;
using Modelo;
using Grafo;
using System.Collections;

namespace Control
{
    public class Aplicacion
    {

        private StreamWriter _escritor;

        public StreamWriter Escritor
        {
            get { return _escritor; }
            set { _escritor = value; }
        }
        private String rutaD;

        public String RutaD
        {
            get { return rutaD; }
            set { rutaD = value; }
        }

        private Simulacion _simulacion;
        private string ruta;
        private int _cantidadPasajeros = 1000;
        

        //propiedades propias de la simulacion
        private List<Pasajero> _pasajerosTemporalIngreso;
        private Queue<Pasajero> _pasajerosIngresoE;
        private List<Bus> _rutasSimulacion;
        //Esta lista contiene todos los buses de la simulacion
        private List<Bus> _BusesSimulacion;
        public static void Main(String[] args)
        {
            
        }

        public Aplicacion()
        {
            _simulacion = new Simulacion();
            _pasajerosIngresoE = new Queue<Pasajero>();
            _pasajerosTemporalIngreso = new List<Pasajero>();
            _rutasSimulacion = new List<Bus>();
            _BusesSimulacion = new List<Bus>();
        }

        public void generarRegistro(String linea)
        {
            _escritor.WriteLine(linea);
            _escritor.WriteLine();
        }

        public void abrirFlujo()
        {
            _escritor = new StreamWriter(rutaD);
        }

        public void cerrarFlujo(StreamWriter flujo)
        {
            flujo.Close();
        }

        public void generarPasajeros2()
        {
            _pasajerosIngresoE.Clear();
            Random gen = new Random();
            List<Estacion> estaciones = _simulacion.darEstacionesDefinitivas();

		 double undsTiempo = 1200;
		 //double eventos = 1000;
         double eventos = (double)_cantidadPasajeros;
		 double lambda = eventos / undsTiempo;
		 double tiempoActual = 0;
		 double pasajerosGenerados = 0;
         for (int i = 0; i < undsTiempo; i++)
         {
             if (i == Math.Ceiling(tiempoActual))//
             {
                 for (int j = 0; j < lambda; j++)
                 {
                     double time = -Math.Log(1 - gen.NextDouble()) / lambda;
                     tiempoActual += time;
                     pasajerosGenerados++;
                 }

                 for (int k = 0; k <= pasajerosGenerados; k++)
                 {
                     double tiempo = tiempoActual;
                     Estacion inicio = estaciones[gen.Next(estaciones.Count)];
                     Estacion fin = estaciones[gen.Next(estaciones.Count)];
                     Pasajero pasajeroNuevo = new Pasajero(tiempo, inicio, fin);
                     List<String>rutaASeguir=_simulacion.darMejorRutaATomar(inicio.darNombre(),fin.darNombre());
                     List<Ruta> rutaAtomar = new List<Ruta>();
                     foreach(string r in rutaASeguir){
                         Ruta rut=_simulacion.encontarRutaDef(r);
                         rutaAtomar.Add(rut);
                     }
                     pasajeroNuevo.definirRumbo(rutaAtomar);
                     _pasajerosIngresoE.Enqueue(pasajeroNuevo);          
                 }
                 pasajerosGenerados = 0;
             }
         }
        }

       

        /*public void generarPasajeros()
        {
            Random gen = new Random();
            List<Estacion> estaciones = _simulacion.darEstacionesDefinitivas();
            for (int contar = 0;contar < _cantidadPasajeros;contar++){
               double generado = gen.NextDouble();
               int tiempo = 0;
               double factor = 110;
               tiempo = (int) Math.Ceiling((-Math.Log(generado)) / (factor));
               tiempo = gen.Next(1400);
               Estacion inicio = estaciones[gen.Next(estaciones.Count)];
               Estacion fin = estaciones[gen.Next(estaciones.Count)];
               Pasajero pasajeroNuevo = new Pasajero(tiempo, inicio, fin);
               _pasajerosTemporalIngreso.Add(pasajeroNuevo);               
            }
            organizarPasajeros();
        }
         * */

        public void despacharBuses(int tiempo){

            foreach(Bus bus in _rutasSimulacion){
                double frecuencia=bus.darFrecuencia();
                double resultado= tiempo % frecuencia;
                int flotaTotal = bus.darFlotaTotal();
                int flotaTemp = bus.darFlotaTemp();
                if (resultado == 0 && flotaTemp>0)
                {
                    Bus b = new Bus(bus.darPlaca(), bus.darOrientacion(), bus.darCapacidad(), bus.darSobrecupo(), bus.darVelocidad(), bus.darRuta(), bus.darFlotaTotal(), bus.darFrecuencia());
                    _BusesSimulacion.Add(b);
                    flotaTemp = flotaTemp - 1;
                    bus.cambiarFlotaTemp(flotaTemp);
                }
            }
        }

        public void moverBuses()
        {
            foreach (Bus bus in _rutasSimulacion)
            {
                string resp=bus.avanzar();
                string idResp=_simulacion.buscarIdEstacion(resp);
                if (!resp.Equals("EN VIA"))
                {
                    Estacion e = bus.darRuta().darEstacion(idResp);
                    e.llegoBus(bus);
                }
            }
        }

        private void llenarListaBuses()
        {
            _rutasSimulacion=_simulacion.darBusesPorDia();
        }

        private void organizarPasajeros()
        {           
            List<Pasajero> sortedList = _pasajerosTemporalIngreso.OrderBy(o => o.darHoraLlegada()).ToList();
            for (int contar = 0; contar < sortedList.Count; contar++)
            {
                _pasajerosIngresoE.Enqueue(sortedList[contar]);
            }
        }
 
        //Ingresa los nuevos pasajeros que llegan a una estacion
        public void verificarNuevosPasajeros(int tiempo)
        {
            try
            {
                bool verificacion = true;
                while (verificacion)
                {

                    Pasajero temporal = _pasajerosIngresoE.Peek();
                    if (temporal == null)
                    {
                        verificacion = false;
                    }
                    else
                    {
                        if (temporal.darHoraLlegada() <= tiempo)
                        {
                            Estacion origen = temporal.darEstacionOrigen();
                            origen.ingresarPasajero(_pasajerosIngresoE.Dequeue());
                            generarRegistro("Pasajero ingreso a estacion: " + origen.darNombre());
                        }
                        else
                        {
                            verificacion = false;
                        }
                    }
                }
            }
            catch (Exception) { }
        }

        public void bw_DoWorkEstaciones(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;

            if ((worker.CancellationPending == true))
            {
                e.Cancel = true;
            }
            else
            {
                //loop de de generacion de los registros de los pacientes.
                string stopId = "";
                string longName = "";
                string gpsX = "";
                string gpsY = "";
                bool dentro = true;
                //fin del loop.              
                using (StreamReader sr = new StreamReader(ruta + "//STOPS.txt"))
                    while (dentro)
                    {
                        try
                        {
                            String line = sr.ReadLine();
                            if (line != null)
                            {
                                String[] parametros = line.Split(';');
                                stopId = parametros[0];
                                longName = parametros[3];
                                gpsX = parametros[6];
                                gpsY = parametros[7];
                                char[] nuevoLongName = longName.ToCharArray();
                                StringBuilder stbuild = new StringBuilder();
                                for (int c = nuevoLongName.Length - 4; c < nuevoLongName.Length - 1; c++)
                                {
                                    stbuild.Append(nuevoLongName[c]);
                                }
                                string finNombre = stbuild.ToString();
                                if (finNombre.Equals(" A1") || finNombre.Equals(" A2") || finNombre.Equals(" A3") || finNombre.Equals(" A4") ||
                                    finNombre.Equals(" B1") || finNombre.Equals(" B2") || finNombre.Equals(" B3") || finNombre.Equals(" B4") ||
                                    finNombre.Equals(" C1") || finNombre.Equals(" C2") || finNombre.Equals(" C3") || finNombre.Equals(" C4") ||
                                    finNombre.Equals(" E1") || finNombre.Equals(" E2") || finNombre.Equals(" E3") || finNombre.Equals(" E4"))
                                {
                                    stbuild = new StringBuilder();
                                    for (int c = 1; c < nuevoLongName.Length - 4; c++)
                                    {
                                        stbuild.Append(nuevoLongName[c]);
                                    }
                                    _simulacion.agregarEstacion(stopId, stbuild.ToString());
                                    _simulacion.crearEstacionesDefinitivas(stbuild.ToString(), gpsX, gpsY);
                                }
                            }
                            else
                            {
                                dentro = false;
                            }
                            int progress = Convert.ToInt32((sr.BaseStream.Position + 1) / sr.BaseStream.Length);
                            worker.ReportProgress(progress);
                        }
                        catch (Exception) { }
                    }
            }
        }

        public void bw_DoWorkLineas(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;

            if ((worker.CancellationPending == true))
            {
                e.Cancel = true;
            }
            else
            {
                //loop de de generacion de los registros de los pacientes.            
                string nombre = "";
                string lineaId = "";
                bool dentro = true;
                //fin del loop.  
                using (StreamReader sr = new StreamReader(ruta + "//LINES.txt"))
                    while (dentro)
                    {
                        try
                        {
                            String line = sr.ReadLine();
                            if (line != null)
                            {
                                String[] parametros = line.Split(';');
                                nombre = parametros[2];
                                lineaId = parametros[0];
                                char[] nuevosParametros = nombre.ToCharArray();
                                if (nuevosParametros[1].Equals('T') || nuevosParametros[1].Equals('E'))
                                {
                                    _simulacion.agregarLinea(lineaId, nombre);
                                    //Console.WriteLine(lineaId + " - " + nombre);
                                }
                            }
                            else
                            {
                                dentro = false;
                            }
                            int progress = Convert.ToInt32((sr.BaseStream.Position + 1) / sr.BaseStream.Length);
                            worker.ReportProgress(progress);
                        }
                        catch (Exception) { }
                    }
            }
        }

        public void bw_DoWorkLineasStops(object sender, DoWorkEventArgs e)
        {
            _simulacion.inicializarRutas();
            BackgroundWorker worker = sender as BackgroundWorker;

            if ((worker.CancellationPending == true))
            {
                e.Cancel = true;
            }
            else
            {
                //LINESTOPID";"STOPSEQUENCE";"ORIENTATION";"LINEID";"STOPID";"
                string lineId = "";
                string stopId = "";
                string orientacion = "";
                bool dentro = true;
                using (StreamReader sr = new StreamReader(ruta + "//LINESTOPS.txt"))
                    while (dentro)
                    {
                        try
                        {
                            String line = sr.ReadLine();
                            if (line != null)
                            {
                                String[] parametros = line.Split(';');
                                lineId = parametros[3];
                                stopId = parametros[4];
                                orientacion = parametros[2];
                                _simulacion.agregarStopLinea(lineId, stopId,orientacion);
                            }
                            else
                            {
                                dentro = false;
                            }
                            int progress = Convert.ToInt32((sr.BaseStream.Position + 1) / sr.BaseStream.Length);
                            worker.ReportProgress(progress);
                        }
                        catch (Exception) { }
                    }
            }
        }

        public void bw_DoWorkLineaArcos(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;

            if ((worker.CancellationPending == true))
            {
                e.Cancel = true;
            }
            else
            {
                //"LINEARCID";"LINEID";"ARCID";"ARCSEQUENCE";
                //"ORIENTATION";"PLANVERSIONID";"LINEVARIANT";"REGISTERDATE"
                string idLinea = "";
                string idArco = "";
                bool dentro = true;
                using (StreamReader sr = new StreamReader(ruta + "\\LINESARCS.txt"))
                    while (dentro)
                    {
                        try
                        {
                            string line = sr.ReadLine(); // se salta la primera linea que no tiene datos
                            int progress = Convert.ToInt32(((sr.BaseStream.Position + 1) / sr.BaseStream.Length) * 100);
                            worker.ReportProgress(progress);
                            if (line != null)
                            {
                                String[] parametros = line.Split(';');
                                idLinea = parametros[1];
                                idArco = parametros[2];

                                if (idLinea.Equals("131") || idLinea.Equals("140") || idLinea.Equals("142")
                                    || idLinea.Equals("150") || idLinea.Equals("421") || idLinea.Equals("427")
                                    || idLinea.Equals("431") || idLinea.Equals("437") || idLinea.Equals("1471")
                                    || idLinea.Equals("1472") || idLinea.Equals("1571") || idLinea.Equals("131"))
                                {
                                    _simulacion.agregarArcoLinea(idArco, idLinea);
                                }
                            }
                            else
                            {
                                dentro = false;
                            }
                        }
                        catch (Exception) { }
                    }
            }
        }

        public void bw_DoWorkArcos(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;

            if ((worker.CancellationPending == true))
            {
                e.Cancel = true;
            }
            else
            {
                //"ARCID";"PLANVERSIONID";"STOPS_STOPID_START";"STOPS_STOPID_END";
                //"STARTPOINT";"ENDPOINT";"DESCRIPTION";"ARCLENGTH";"AVERAGETIME"
                string startId = "";
                string stopId = "";
                string descripcion = "";
                string tiempo = "";
                string idArco = "";

                string[] lines = System.IO.File.ReadAllLines(ruta + "//ARCS.txt");

                int contador = 0;
                int contadorProgreso = 0;
                int total = lines.Length;
                foreach (string line in lines)
                {
                    contador++;
                    if ((contador % (total / 100)) == 0)
                    {
                        contadorProgreso++;
                        worker.ReportProgress(contadorProgreso);
                    }
                    String[] parametros = line.Split(';');
                    if (parametros.Length > 2)
                    {
                        idArco = parametros[0];
                        startId = parametros[2];
                        stopId = parametros[3];
                        descripcion = parametros[6];
                        tiempo = parametros[7];// se va a usar ARCLENGTH dado que en muchas lineas no existe AVERAGETIME
                        // por ende, en el momento de interpretar esta distancia hay que transformala
                        _simulacion.agregarArcos(startId, stopId, tiempo, idArco);
                    }
                }                
            }
           
        }

        public void bw_DoWorkInstanciacion(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;

            if ((worker.CancellationPending == true))
            {
                e.Cancel = true;
            }
            else
            {
                _simulacion.generarInstanciasFinales();
                generarPasajeros2();
                llenarListaBuses();
                Console.WriteLine(_pasajerosIngresoE.LongCount());
            }
        }

        public void bw_DoWorkFrecuenciaBuses(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;

            if ((worker.CancellationPending == true))
            {
                e.Cancel = true;
            }
            else
            {
                // [LINEA];[DIA];[FRECUENCIA];[FLOTA_OPERATIVA];[VELOCIDAD]
                string linea = "";
                string dia = "";
                string frecuencia = "";
                string flota = "";
                string velocidad = "";

                string[] lines = System.IO.File.ReadAllLines(ruta + "//FRECUENCIA.txt");

                int contador = 0;
                int total = lines.Length;
                foreach (string line in lines)
                {
                    contador++;
                    worker.ReportProgress((contador/total));
                    if (line.Length > 1)
                    {
                        String[] parametros = line.Split(';');
                        if (parametros[1].Length == 1)
                        {
                            linea = parametros[0];
                            dia = parametros[1];
                            frecuencia = parametros[2];
                            flota = parametros[3];
                            velocidad = parametros[4];
                            _simulacion.agregarFrecuencia(linea, dia, frecuencia, flota, velocidad);
                        }
                    }
                }
            }
        }

        public void setCamino(string camino)
        {
            ruta = camino;
        }

        public double[] darCoordenadas()
        {
            List<double> preSalida = new List<double>();
            List<Estacion> _estacionesDef = _simulacion.darEstacionesDefinitivas();
            foreach (Estacion estacionTemporal in _estacionesDef)
            {
                preSalida.Add(estacionTemporal.darGpsY());
                preSalida.Add(estacionTemporal.darGpsX());
            }
            return preSalida.ToArray();
        }

        public string[] darNombresCoordenadas()
        {
            List<string> preSalida = new List<string>();
            List<Estacion> _estacionesDef = _simulacion.darEstacionesDefinitivas();
            foreach (Estacion estacionTemporal in _estacionesDef)
            {
                preSalida.Add(estacionTemporal.darNombre());
            }

            return preSalida.ToArray();
        }

        public string[] darNombresRutas()
        {
            return _simulacion.darNombresLineas().ToArray();
        }

        public string[] darParadasRuta(string nombreRuta)
        {
            return _simulacion.darParadasRuta(nombreRuta);
        }

        public void setParametrosCarga(int capacidad, int opcionDia, int cantidadPasajeros, int capacidadBuses, int sobrecupo)
        {
            _simulacion.setCapacidadParametro(capacidad);
            _cantidadPasajeros = cantidadPasajeros;
            _simulacion.setParametrosBuses(capacidadBuses, sobrecupo);
            _simulacion.setParametrosDia(opcionDia);
        }

        //Verifica e ingresa si hay pasajeros por acceder a las estaciones
        public void verificarEstaciones()
        {
            List<Estacion> estaciones = _simulacion.darEstacionesDefinitivas();
            foreach (Estacion estacionT in estaciones)
            {
                estacionT.revisarFila();
            }
        }

        

        public int darPasajerosActivos()
        {
            int salida = 0;
            List<Estacion> estaciones = _simulacion.darEstacionesDefinitivas();
            foreach (Estacion estacionT in estaciones)
            {
                salida += estacionT.darPasajerosActivos();
            }
            return salida;
        }

        public int darNumeroEstaciones()
        {
            int salida = 0;
            List<Estacion> estaciones = _simulacion.darEstacionesDefinitivas();
            salida = estaciones.Count;
            return salida;
        }

        public Simulacion darSimulacion()
        {
            return _simulacion;
        }

        public void reiniciarListas()
        {
            foreach(Estacion estacion in _simulacion.darEstacionesDefinitivas())
            {
                estacion.reiniciarListas();
            }
        }
    }
}